package telex;
/*
 * IQFReader.java
 *
 * Copyright (c) 2007-2008 Colin Bayer, Douglas Downey, Oren Etzioni,
 *   University of Washington Computer Science and Engineering
 * 
 * See the file "COPYING" in the root directory of this distribution for
 * full licensing information.
 */

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.FileNotFoundException;
import java.io.IOException;

/*
 * IQF (Intermediate Query File) format:
 * 
 * IQF <number of queries in file>
 * <arity of query n-gram>\t<words of n-gram queried, tab-separated>\t<count, or -1 if not resolved yet>[\t<data for internal use>]
 * 		...
 */

public class IQFReader {
	public static class BadIQFException extends RuntimeException {
		private static final long serialVersionUID = 6942073336970233049L;
		public BadIQFException() {}
		public BadIQFException(String message) { super(message); }
	}

	public IQFReader(String aPath) throws FileNotFoundException, IOException {
		mPath = aPath;
		mReader = new BufferedReader(new FileReader(aPath));

		parseHeader();
	}

	private int parseHeader() throws BadIQFException, IOException {
		String hdr = mReader.readLine();
		String[] fields = hdr.split(" ");

		if (!fields[0].equals("IQF")) {
			throw new BadIQFException("Bad magic in header of IQF file " + mPath);
		} else {
			try {
				int query_ct = Integer.parseInt(fields[1]);

				if (query_ct < 0)
					throw new BadIQFException("Bad query count in header of IQF file " + mPath);

				mNQueries = query_ct;
				mQueryIdx = 0;

				return query_ct;
			} catch (Exception e) {
				throw new BadIQFException("Bad query count in header of IQF file " + mPath);
			}	
		}
	}

	/*
	public Query getQuery(int aIdx) {
		if (aIdx < 0 || aIdx >= mQueries.length) return null;
		
		return mQueries[aIdx];
	}

	public Query[] getQueries() {
		return mQueries;
	}*/
	
	public String getPath() { return mPath; }
	public long getQueryCount() { return mNQueries; }
	
	public Query nextQuery() throws IOException {
		String q = mReader.readLine();
		Query rv;
		
		if (q == null && mQueryIdx < mNQueries) {
			// too few queries; throw a BadIQF.
			throw new BadIQFException("Fewer queries than expected in IQF file " + mPath);
		} else if (q != null && mQueryIdx >= mNQueries) {
			// too many queries; warn once and then suppress the warning and skip the queries.
			
			if (!mSkipWarningSuppressed)
				System.out.println("Warning: more queries than expected in IQF file " + mPath + "; additional queries ignored");
			
			mSkipWarningSuppressed = true;
			rv = null;
		} else if (q == null) {
			rv = null;
		} else {
			// new query.
			
			// grab search arity.
			String[] first_split = q.split("\t", 2);
			int arity;
			
			try {
				arity = Integer.parseInt(first_split[0]);
			} catch (Exception e) {
				throw new BadIQFException("Non-numeric arity in query " + mQueryIdx + " of IQF file " + mPath);
			}
			
			// split remainder of line into arity + 1 (for result) + 1 (for user data) fields.
			String[] words = new String[arity];
			String[] second_split = first_split[1].split("\t", arity + 2);
			long result;
			
			if (second_split.length < arity + 1) {
				throw new BadIQFException("Unexpected number of fields in query " + mQueryIdx + " of IQF file " + mPath);
			}
			
			// copy aside search n-gram for building the Query object.
			System.arraycopy(second_split, 0, words, 0, arity);
			
			// parse result count.
			try {
				result = Long.parseLong(second_split[arity]);
			} catch (Exception e) {
				throw new BadIQFException("Non-numeric result in query " + mQueryIdx + " of IQF file " + mPath);
			}
			
			rv = new Query(words, result, (second_split.length == arity + 2) ? second_split[arity + 1] : null);
			mQueryIdx++;
		}

		return rv;
	}
	
	public boolean close() {
		try {
			mReader.close();
		} catch (Exception e) {
			System.err.println("Error closing IQF file " + mPath + " while reading: " + e);
			return false;
		}
		
		return true;
	}

	private int mNQueries;
	private int mQueryIdx;
	
	private boolean mSkipWarningSuppressed = false;
	
	private String mPath;
	private BufferedReader mReader;
	
	/* testing code.  and not good testing code, at that. */
	public static void main(String[] aArgs) {
		if (aArgs.length < 1) {
			System.out.println("Usage: java IQFReader <IQF file>");
			System.exit(255);
		}
		
		try {
			IQFReader iqf = new IQFReader(aArgs[0]);
			
			//Query[] qs = iqf.getQueries();
			Query q;
			
			while ((q = iqf.nextQuery()) != null) {
				System.out.println(q);
			}
			
			System.out.println("Reading IQF file successful.");
			System.exit(0);
		} catch (Exception e) {
			System.out.println(e);
			System.out.println("Reading IQF file failed.");
			System.exit(1);
		}
	}
}
